//Copyright 2010 Balazs Keri, Imre Keri
//
//This file is part of FireworksCreator.
//
//FireworksCreator is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//FireworksCreator is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with FireworksCreator.  If not, see <http://www.gnu.org/licenses/>.
//
#include <global.h>
#include "particletemplate.h"
#include "emittertemplate.h"
#include "shooter.h"
#include "group.h"
#include "root.h"
//
namespace model
{
//
Root::Root(QObject *p_parent)
:
   QObject(p_parent),
   root_group(0)
{
}
Root::~Root()
{
   delete_contents();
}
void Root::delete_contents()
{
   QSetIterator<ParticleTemplate*> pti(particle_templates);
   while(pti.hasNext())delete pti.next();
   particle_templates.clear();
   //
   QSetIterator<EmitterTemplate*> eti(emitter_templates);
   while(eti.hasNext())delete eti.next();
   emitter_templates.clear();
   //
   QSetIterator<Shooter*> si(shooters);
   while(si.hasNext())delete si.next();
   shooters.clear();
   //
   QSetIterator<Group*> gi(groups);
   while(gi.hasNext())delete gi.next();
   groups.clear();
   delete root_group;
   root_group=0;
   //
   child.clear();
   parent.clear();
}
//
void Root::store_data(QDataStream &p_stream)const
{
   //TODO
}
void Root::restore_data(RestoreStream &p_stream)
{
   //TODO
}
//
void Root::test_invariant()const
{
   Q_ASSERT(root_group!=0);
   Q_ASSERT(!groups.contains(root_group));
   //
   QSet<Identified*> all_objects;
   all_objects.insert(root_group);
   QSetIterator<ParticleTemplate*> pti(particle_templates);
   while(pti.hasNext())
   {
      ParticleTemplate *pt=pti.next();
      Q_ASSERT(pt!=0);
      all_objects.insert(pt);
   }
   QSetIterator<EmitterTemplate*> eti(emitter_templates);
   while(eti.hasNext())
   {
      EmitterTemplate *et=eti.next();
      Q_ASSERT(et!=0);
      all_objects.insert(et);
   }
   QSetIterator<Shooter*> si(shooters);
   while(si.hasNext())
   {
      Shooter *s=si.next();
      Q_ASSERT(s!=0);
      all_objects.insert(s);
   }
   QSetIterator<Group*> gi(groups);
   while(gi.hasNext())
   {
      Group *g=gi.next();
      Q_ASSERT(g!=0);
      all_objects.insert(g);
   }
   //
   for(ContainmentType::const_iterator itr=child.begin();itr!=child.end();++itr)
   {
      Identified * const parent=itr.key();
      const IdentifiedList &children=itr.value();
      Q_ASSERT(all_objects.contains(parent));
      QSet<const Identified*> set;
      for(IdentifiedList::const_iterator child=children.begin();child!=children.end();++child)
      {
         Q_ASSERT(*child!=parent);
         Q_ASSERT(all_objects.contains(*child));
         Q_ASSERT(!set.contains(*child));
         ContainmentType::const_iterator parent1=this->parent.find(*child);
         Q_ASSERT(parent1!=this->parent.end());
         Q_ASSERT(parent1.value().contains(parent));
         set.insert(*child);
      }
   }
   for(ContainmentType::const_iterator itr=parent.begin();itr!=parent.end();++itr)
   {
      Identified * const child=itr.key();
      const IdentifiedList &parents=itr.value();
      Q_ASSERT(all_objects.contains(child));
      Q_ASSERT(!parents.empty());
      QSet<const Identified*> set;
      for(IdentifiedList::const_iterator parent=parents.begin();parent!=parents.end();++parent)
      {
         Q_ASSERT(*parent!=child);
         Q_ASSERT(all_objects.contains(*parent));
         Q_ASSERT(!set.contains(*parent));
         ContainmentType::const_iterator child1=this->child.find(*parent);
         Q_ASSERT(child1!=this->child.end());
         Q_ASSERT(child1.value().contains(child));
         set.insert(*parent);
      }
   }
   //
   all_objects.remove(root_group);
   QSetIterator<Identified*> itr(all_objects);
   while(itr.hasNext())
   {
      Identified *object=itr.next();
      Q_ASSERT(parent.find(object)!=parent.end());
   }
}
//
QString Root::get_object_name(const Identified &p_object)
{
   if(const ParticleTemplate *pt=qobject_cast<const ParticleTemplate*>(&p_object))
   {
      return pt->get_name();
   }
   if(const EmitterTemplate *et=qobject_cast<const EmitterTemplate*>(&p_object))
   {
      return et->get_name();
   }
   if(const Shooter *s=qobject_cast<const Shooter*>(&p_object))
   {
      return s->get_name();
   }
   if(const Group *g=qobject_cast<const Group*>(&p_object))
   {
      return g->get_name();
   }
   return "";
}
Root::IdentifiedList Root::get_children(const Identified &p_object) const
{
   ContainmentType::const_iterator const children=child.find(const_cast<Identified*>(&p_object));
   if(children==child.end())
   {
      return IdentifiedList();
   }
   return children.value();
}
Root::IdentifiedList Root::get_parents(const Identified &p_object) const
{
   if(&p_object==root_group)
   {
      return IdentifiedList();
   }
   return parent.value(const_cast<Identified*>(&p_object));
}
bool Root::test_containment(const Identified &p_parent,const Identified &p_child)const
{
   if(&p_child==root_group)
   {
      return false;
   }
   ContainmentType::const_iterator itr=parent.find(const_cast<Identified*>(&p_child));
   for(IdentifiedList::const_iterator parent=itr.value().begin();parent!=itr.value().end();++parent)
   {
      if(&p_parent==*parent)
         return true;
      if(test_containment(p_parent,*parent))
         return true;
   }
   return false;
}
//
}
